/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 北京银杉金服科技有限公司
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2018/9/8 22:09
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.security;

import com.google.common.collect.Lists;
import com.hyacinth.util.AppConfigurer;
import com.hyacinth.util.XaUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;

/**
 * 提供某个资源对应的权限定义，即getAttributes方法返回的结果。
 * 此类在初始化时，应该取到所有资源及其对应角色的定义。
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
@Component
@Slf4j
@Order(value = 1) //控制配置类的加载顺序,越小越先加載
public class CustomSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, CommandLineRunner {

    private final UrlCache urlCache;
    @Autowired
    public CustomSecurityMetadataSource(UrlCache urlCache) {
        this.urlCache = urlCache;
    }

    /**排除URL前缀数组*/
    private static String [] excludedUrlPrefix;
    /**所有角色数组*/
    private static String[] roles;
    /**资源角色映射{url@method ：["ROLE1","ROLE1","ROLE1"]}*/
    private static HashMap<String,Set<String>> urlRoles;

    /**
     * 根据URL，找到相关的权限配置
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        FilterInvocation fi = (FilterInvocation) object;
        HttpServletRequest request = fi.getRequest();
        String servletPath = request.getServletPath();
        String url = fi.getRequestUrl();
        String method = request.getMethod();
        // 静态资源检查
        String staticCheck = servletPath.toLowerCase();
        if (ArrayUtils.isNotEmpty(excludedUrlPrefix)) {
            for(String exclude : excludedUrlPrefix){
                if(staticCheck.startsWith(exclude)){
                    log.info("请求url={}，为静态资源，不受限制", url);
                    return SecurityConfig.createList();
                }
            }
        }
        // 正常的urls
        Set<String> urlSet = urlRoles.keySet();
        String regexUrl = SecurityUtils.getRegexUrl(servletPath + "@" + method, urlSet);
        if (StringUtils.isNotBlank(regexUrl)) {
            Set<String> roleList = urlRoles.get(regexUrl);
            if (!XaUtils.isEmpty(roleList)) {
                return SecurityConfig.createList(roleList.toArray(new String[roleList.size()]));
            }
        }
        log.info("该请求url={}，开放权限平台所有角色={}", url, roles);
        return SecurityConfig.createList();
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return Lists.newArrayList();
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    /**
     * 项目启动初始化
     * 加载资源信息
     */
    @Override
    public void run(String... args) throws Exception {
        // 加载资源角色Map
        urlCache.loadUrlRoles();
        roles = urlCache.getRoles();
        urlRoles = urlCache.getUrlRoles();
        // 获取配置不包含授权校验的urls
        String exclUrlPrefixStr = AppConfigurer.getProperties("excluded.url.prefix");
        if (StringUtils.isNotBlank(exclUrlPrefixStr)) {
            if(exclUrlPrefixStr.contains(",")){
                excludedUrlPrefix = exclUrlPrefixStr.split(",");
            } else {
                excludedUrlPrefix = new String[]{exclUrlPrefixStr};
            }
        }
    }
}
